//
//  BBTUIOEventDelegate.m
//  BBTouch
//
//  Created by ben smith on 7/22/08.
//  This file is part of BBTouch.
//
//  BBTouch is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.

//  BBTouch is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.

//  You should have received a copy of the GNU Lesser General Public License
//  along with BBTouch.  If not, see <http://www.gnu.org/licenses/>.
// 
//  Copyright 2008 Ben Britten Smith ben@benbritten.com .
//

#import "BBTUIOEventDelegate.h"
#import "BBBlobEvent.h"
#import "BBMeshController.h"
#import "BBDistortionMap.h"

// grab all the OSC stuff
#import "BBOSCArgument.h"
#import "BBOSCListener.h"
#import "BBOSCDispatcher.h"
#import "BBOSCDataUtilities.h"
#import "BBOSCMessage.h"
#import "BBOSCBundle.h"
#import "BBOSCDecoder.h"
#import "BBOSCSender.h"
#import "BBOSCAddress.h"

#define BBB_TUIO_PROFILE_ADDRESS @"/tuio/2Dcur"


@implementation BBTUIOEventDelegate

// this is the object that will handle the generation of TUIO formatted events and send
// them to an OSC datagram stream

// from what i gan decipher from the TUIO docs and code here is the general scheme:

// for our purposes right now, we do not have any fiducials, so all our blobs are
// classified as '2d cursor objects' in the TUIO world, so we will be using the
// TUIO profile: /tuio/2Dcur set s x y X Y m
// where:
// s 	sessionID, temporary object ID, int32
// x, y 	position, float32, range 0...1
// X, Y  	movement vector (motion speed & direction), float32
// m 	motion acceleration, float32

// now that is groovy, so for every blob on teh surface i need at least one call like the above.
// then i need to generate a few other messages and bundle them up
// first ID myself
// /tuio/[profileName] source name
//
// then send all my object states
// /tuio/[profileName] set [sessionID parameterList]
//
// then send an 'alive' message which identifies all the objects that are currently active
// /tuio/[profileName] alive [list of active sessionIDs]
//
// then send some frame ref number
// /tuio/[profileName] fseq [int32]

// hopefully this is all correct, the docs arent very specific, and the sample code is not 
// well commented

// what this means is that we need to synthesize the above data relying on the
// BBTouch blob events which are blobDown, blobDragged, blobUp, and blobMovementFinished
// since the BBTouch blob events dont do anything if the blob is active and unmoving
// then i will need to keep track of the 'alive' blobs myself 

// this is very similar to the simpleEventView

@synthesize tuioSender;
@synthesize meshBounds;

// the easy way to get a tuio event delegate
+(BBTUIOEventDelegate*)eventDelegateWithHostName:(NSString*)host portNumber:(int)portNumber
{
	BBTUIOEventDelegate * newDelegate = [[BBTUIOEventDelegate alloc] init];
	BBOSCSender * newSender = [BBOSCSender senderWithDestinationHostName:host portNumber:portNumber];
	[newDelegate setTuioSender:newSender];
	return [newDelegate autorelease];
}


// the init method
// also, dont forget to set the sender object, or it wont send anything
- (id) init
{
	self = [super init];
	if (self != nil) {
		livingBlobs = [[NSMutableArray alloc] init];
		movingBlobs = [[NSMutableArray alloc] init];
		frameNumber = 0; // seems a good place to start
		// build a re-usable source message
		sourceMessage = [[BBOSCMessage alloc] initWithBBOSCAddress:[BBOSCAddress addressWithString:BBB_TUIO_PROFILE_ADDRESS]];
		[sourceMessage attachArgument:[BBOSCArgument argumentWithString:@"source"]];
		[sourceMessage attachArgument:[BBOSCArgument argumentWithString:@"BBTouch"]];
	}
	return self;
}


// blob down, add the blob to my own list of blob events
-(void)blobDown:(BBBlobEvent*)theBlobEvent; 
{
//	NSLog(@"Blob %d Down at: %@",[theBlobEvent blobID],NSStringFromPoint([theBlobEvent screenLocation]));
	// add the blob to my list, be sure to make a copy
	[livingBlobs addObject:[[theBlobEvent copy] autorelease]];
	// also add to the moving array, since 'down' is considered a move
	[movingBlobs addObject:[[theBlobEvent copy] autorelease]];
	[super blobDown:theBlobEvent];
}

-(void)blobDragged:(BBBlobEvent*)theBlobEvent
{
	// blob state change, add it to the array of moving blobs
//	NSLog(@"Blob %d Dragged To: %@",[theBlobEvent blobID],NSStringFromPoint([theBlobEvent screenLocation]));
	[movingBlobs addObject:[[theBlobEvent copy] autorelease]];
	[super blobDragged:theBlobEvent];
}

-(void)blobUp:(BBBlobEvent*)theBlobEvent;
{
//	NSLog(@"Blob %d Up at: %@",[theBlobEvent blobID],NSStringFromPoint([theBlobEvent screenLocation]));
	int blobIndex = [self indexOfBlobWithID:[theBlobEvent blobID]];
	if (blobIndex != NSNotFound) {
		[livingBlobs removeObjectAtIndex:blobIndex];
	}
	[super blobUp:theBlobEvent];
}

-(void)blobMovementFinished;
{
//	NSLog(@"Blob movement finished");
	[super blobMovementFinished];
	
	// ok, all the blobs are done moving about for this frame, so here we build the TUIO messages and bundle them together
	// grab the current mesh frame so we can normalize the points properly for TUIO
	// this is kinda ugly
	// also these should really ony change when the actual mesh changes so we shouldnt need
	// to get them every frame
	self.meshBounds = (NSRect)[[[BBMeshController sharedMeshController] distortionMap] mapFrame];
		
	BBOSCBundle * tuioBundle = [BBOSCBundle bundleWithTimestamp:[NSDate date]];
	
	// now add the source message
	[tuioBundle attachObject:sourceMessage];

	// now add all my blob state changes
	for (BBBlobEvent* anEvent in movingBlobs) {
		[tuioBundle attachObject:[self setMessageForBlobEvent:anEvent]];
	}
	
	// now add the alive message
	[tuioBundle attachObject:[self aliveMessage]];

	[tuioBundle attachObject:[self frameSequenceMessage]];
	
	// ok, we should be all done
	[tuioSender sendOSCPacket:tuioBundle];

	// clear out the moving blobs array
	// increment my frame number
	frameNumber ++;
	[movingBlobs removeAllObjects];
}

// generates a single 'alive' message for all the living blobs
-(BBOSCMessage*)frameSequenceMessage
{
	BBOSCMessage *frameSequenceMessage = [BBOSCMessage messageWithBBOSCAddress:[BBOSCAddress addressWithString:BBB_TUIO_PROFILE_ADDRESS]];
	[frameSequenceMessage attachArgument:[BBOSCArgument argumentWithString:@"fseq"]];
	[frameSequenceMessage attachArgument:[BBOSCArgument argumentWithInt:frameNumber]];
	return frameSequenceMessage;
}



// generates a single 'alive' message for all the living blobs
-(BBOSCMessage*)aliveMessage
{
	BBOSCMessage *aliveMessage = [BBOSCMessage messageWithBBOSCAddress:[BBOSCAddress addressWithString:BBB_TUIO_PROFILE_ADDRESS]];
	[aliveMessage attachArgument:[BBOSCArgument argumentWithString:@"alive"]];
	
	for (BBBlobEvent* anEvent in livingBlobs) {
		[aliveMessage attachArgument:[BBOSCArgument argumentWithInt:[anEvent blobID]]];
	}
	
	return aliveMessage;
}


// generates a single 'set' message for a single blob
-(BBOSCMessage*)setMessageForBlobEvent:(BBBlobEvent*)anEvent
{
	BBOSCMessage *setMessage = [BBOSCMessage messageWithBBOSCAddress:[BBOSCAddress addressWithString:BBB_TUIO_PROFILE_ADDRESS]];
	[setMessage attachArgument:[BBOSCArgument argumentWithString:@"set"]];
	// s 	sessionID, temporary object ID, int32
	// x, y 	position, float32, range 0...1
	// X, Y  	movement vector (motion speed & direction), float32
	// m 	motion acceleration, float32
	[setMessage attachArgument:[BBOSCArgument argumentWithInt:[anEvent blobID]]];

	NSPoint blobLoc = [anEvent screenLocation];
	
	[setMessage attachArgument:[BBOSCArgument argumentWithFloat:blobLoc.x]];
	[setMessage attachArgument:[BBOSCArgument argumentWithFloat:(1.0 - blobLoc.y)]]; // flip the y, TUIO is opposite of BBTouch
	// the movement vector is the speed at which the blob is moving
	// this is simply the distance moved over the time (in this case, 30FPS, hardcoded for now)
	float xVelo = [anEvent deltaX] * 30.0 * -1.0;
	float yVelo = [anEvent deltaY] * 30.0;
	
	[setMessage attachArgument:[BBOSCArgument argumentWithFloat:xVelo]];
	[setMessage attachArgument:[BBOSCArgument argumentWithFloat:yVelo]];
	
	// now for the acceleration
	float accel = [anEvent acceleration] * 30.0;
	[setMessage attachArgument:[BBOSCArgument argumentWithFloat:accel]];
	return setMessage;
}


// quick convenience method to grab blob indexes
-(int)indexOfBlobWithID:(int)blobId
{
	int blobIndex;
	for (blobIndex = 0; blobIndex < [livingBlobs count]; blobIndex++) 
		if ([[livingBlobs objectAtIndex:blobIndex] blobID] == blobId) return blobIndex;
	return NSNotFound;	
}

@end
